﻿using Microsoft.Kinect;
using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;
using System;
using System.IO;
using System.Diagnostics;
namespace Imperium
{
    class KinectSpeechEngine
    {
        private Kinect kinect;
        private Grammar name;
        private ImperiumModel model;
        private SpeechRecognitionEngine speechEngine;
        public String State;

        /// <summary>
        /// Constructor
        /// </summary>
        public KinectSpeechEngine(ImperiumModel m, Kinect kinect, Grammar name)
        {
            this.model = m;
            this.kinect = kinect;
            this.name = name;

            if (kinect.Sensor != null)
            {
                SetupSpeechEngine();
            }
            else
            {
                throw new Exception("Kinect not setup.");
            }
        }

        /// <summary>
        /// Setup Speech Recognizer for Kinect and Load Grammar with Name Command
        /// </summary>
        public void SetupSpeechEngine()
        {
            try
            {
                RecognizerInfo ri = GetKinectRecognizer();

                if (null != ri)
                {
                    this.speechEngine = new SpeechRecognitionEngine(ri.Id);
                    this.speechEngine.SpeechRecognized += SpeechRecognized;
                    this.speechEngine.SetInputToAudioStream(this.kinect.Sensor.AudioSource.Start(), new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));

                    // must load one grammar before the Kinect can be initialized.
                    LoadGrammar(this.name);

                    this.speechEngine.RecognizeAsync(RecognizeMode.Multiple);
                }
                else
                {
                    this.State = Properties.Resources.NoSpeechRecognizer;
                }
            }
            catch
            {
                this.State = Properties.Resources.NoSpeechRecognizer;
            }
        }

        /// <summary>
        /// Gets the metadata for the speech recognizer (acoustic model) 
        /// most suitable to process audio from Kinect device.
        /// </summary>
        /// <returns>
        /// RecognizerInfo if found, null otherwise.
        /// </returns>
        private static RecognizerInfo GetKinectRecognizer()
        {
            foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())
            {
                string value;
                recognizer.AdditionalInfo.TryGetValue("Kinect", out value);
                if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return recognizer;
                }
            }

            return null;
        }

        /// <summary>
        /// Load command(s) into Kinect
        /// </summary>
        public void LoadGrammar(Grammar commands)
        {
            this.speechEngine.UnloadAllGrammars();
            this.speechEngine.LoadGrammar(commands);
        }

        /// <summary>
        /// Handler for recognized speech events.
        /// </summary>
        private void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            this.model.CommandGiven(e.Result.Text.ToUpperInvariant(), e.Result.Confidence);
        }

        /// <summary>
        /// Destructor
        /// </summary>
        ~KinectSpeechEngine()
        {
            if (null != this.speechEngine)
            {
                this.speechEngine.SpeechRecognized -= SpeechRecognized;
                this.speechEngine.RecognizeAsyncStop();
            }
        }
    }
}
